// Copyright (c) 2010 Manuel Peinado Gallego <manuel.peinado@gmail.com>
// Distributed under the MIT license

#include "stdafx.h"
#include "WorldBuilder.h"
#include "Triangle.h"
#include "Ray.h"

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Triangle::Triangle()
: v0(gx::zero), v1(gx::zero), v2(gx::zero)
{
}

Triangle::Triangle(const gx::Vec3 & v0, const gx::Vec3 & v1, const gx::Vec3 & v2)
: v0(v0), v1(v1), v2(v2) 
{
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

// intersect_RayTriangle(): intersect a ray with a 3D triangle
//    Input:  a ray R, and a triangle T
//    Output: *I = intersection point (when it exists)
//    Return: -1 = triangle is degenerate (a segment or point)
//             0 = disjoint (no intersect)
//             1 = intersect in unique point I
//             2 = are in the same plane

int Triangle::rayIntersection(const Ray & ray, gx::Vec3 & interPnt) const
{
	gx::Vec3    u, v, n;             // triangle vectors
	gx::Vec3    dir, w0, w;          // ray vectors
	double     r, a, b;             // params to calc ray-plane intersect

	// get triangle edge vectors and plane normal
	u = v1 - v0;
	v = v2 - v0;
	n = u.cross(v);             // cross product

	
	// TODO maybe throw an exception or use an assert here
	if (n.equals(gx::zero))     // triangle is degenerate
	{
		return -1;			// do not deal with this case
	}

	dir = ray.direction;             // ray direction vector
	w0 = ray.origin - v0;
	a = -n.dot(w0);
	b = n.dot(dir);

	// TODO comment this
	if(b > EPSILON)
	{
		return 0;
	}

	if (fabs(b) < EPSILON) {     // ray is parallel to triangle plane
		if (a == 0)                // ray lies in triangle plane
			return 2;
		else return false;             // ray disjoint from plane
	}

	// get intersect point of ray with triangle plane
	r = a / b;
	if (r < 0.0)                   // ray goes away from triangle
		return 0;                  // => no intersect
	// for a segment, also test if (r > 1.0) => no intersect

	interPnt = ray.origin + r * dir;           // intersect point of ray and plane

	// is I inside T?
	double uu, uv, vv, wu, wv, D;
	uu = u.dot(u);
	uv = u.dot(v);
	vv = v.dot(v);
	w = interPnt - v0;
	wu = w.dot(u);
	wv = w.dot(v);
	D = uv * uv - uu * vv;

	// get and test parametric coords
	double s, t;
	s = (uv * wv - vv * wu) / D;
	if (s < 0.0 || s > 1.0)        // I is outside T
		return 0;
	t = (uv * wu - uu * wv) / D;
	if (t < 0.0 || (s + t) > 1.0)  // I is outside T
		return 0;

	return 1;                      // I is in T
}
